package org.openfdtd.core3d;

import org.openfdtd.core3d.Util;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.ViewingPlatform;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.Group;
import javax.media.j3d.Node;
import javax.media.j3d.TransformGroup;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import org.openfdtd.core3d.behaviors.MouseRotate;
import org.openfdtd.core3d.behaviors.MouseTranslate;
import org.openfdtd.core3d.behaviors.MouseZoom;

/**
 *
 * @author Dorian C. Langbeck
 */
public class BasicUniverse implements Collection<SceneComponent> {
    private ArrayList<SceneComponent> components;
    protected TransformGroup objRotate;
    protected SimpleUniverse universe;
    protected BranchGroup objRoot;
    protected Camera camera;
    private Background bg;

    public BasicUniverse(Canvas3D canvas) {
        this(canvas, null);
    }

    public BasicUniverse(Canvas3D canvas, Color3f bgColor) {
        canvas.setDoubleBufferEnable(true);
        universe = new SimpleUniverse(canvas);
        components = new ArrayList<SceneComponent>();

        objRoot = new BranchGroup();
        objRoot.setCapability(Group.ALLOW_CHILDREN_READ);
        objRoot.setCapability(Group.ALLOW_CHILDREN_WRITE);
        objRoot.setCapability(Group.ALLOW_CHILDREN_EXTEND);

        objRotate = new TransformGroup();
        objRotate.setCapability(Group.ALLOW_CHILDREN_READ);
        objRotate.setCapability(Group.ALLOW_CHILDREN_WRITE);
        objRotate.setCapability(Group.ALLOW_CHILDREN_EXTEND);
        objRotate.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        objRotate.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        objRoot.addChild(objRotate);

        //Regiao de atuacao dos eventos globais do mouse
        BoundingSphere sphere = new BoundingSphere(new Point3d(0, 0, 0), 100);

        //TODO: Ajustar o fator do translate de acordo com o zoom
        MouseRotate mr = new MouseRotate(objRotate);
        mr.setSchedulingBounds(sphere);
        mr.setFactor(0.02); //Ajustar a sensibilidade da rotacao
        objRoot.addChild(mr);

        MouseZoom mz = new MouseZoom(objRotate);
        mz.setSchedulingBounds(sphere);
        mz.setFactor(0.3); //Ajustar a sensibilidade do zoom
        objRoot.addChild(mz);

        MouseTranslate mt = new MouseTranslate(objRotate);
        mt.setSchedulingBounds(sphere);
        mt.setFactor(0.01); //Ajustar a sensibilidade da translação
        objRoot.addChild(mt);

        if (bgColor != null) {
            bg = new Background(bgColor);
            bg.setApplicationBounds(sphere);
            objRotate.addChild(bg);
        }

        setCamera(new Camera());
        universe.addBranchGraph(objRoot);
    }

    public Camera getCamera() {
        return camera;
    }

    public void setCamera(Camera camera) {
        camera.attach(this);
    }

    public Canvas3D getCanvas3D() {
        return universe.getCanvas();
    }

    public ViewingPlatform getViewingPlatform() {
        return universe.getViewingPlatform();
    }
    
    public Background getBackround() {
        return bg;
    }

    //Metodos da interface Collection<SceneComponent>
    public boolean removeAll(Collection<?> c) {
        return components.removeAll(c);
    }

    public boolean containsAll(Collection<?> c) {
        return components.containsAll(c);
    }

    public Iterator<SceneComponent> iterator() {
        return components.iterator();
    }

    public <T> T[] toArray(T[] a) {
        return components.toArray(a);
    }

    public Object[] toArray() {
        return components.toArray();
    }

    public int size() {
        return components.size();
    }

    public boolean isEmpty() {
        return components.isEmpty();
    }

    public boolean contains(Object o) {
        return components.contains(o);
    }

    public boolean addAll(Collection<? extends SceneComponent> c) {
        return components.addAll(c);
    }

    public boolean add(SceneComponent e) {
        if (e.isPickable())
            Util.enablePicking(e);
        objRotate.addChild(e);
        return components.add(e);
    }

    public boolean retainAll(Collection<?> c) {
        return components.retainAll(c);
    }

    public boolean remove(Object o) {
        if (!components.remove(o))
            return false;

        objRotate.removeChild((SceneComponent) o);
        return true;
    }

    public void clear() {
        objRotate.removeAllChildren();
        components.clear();
    }
}
